<!DOCTYPE HTML>
<!--generated with sswg-->
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
    <title>ursinamath</title>
    <link rel="stylesheet" href="../sswg.css">
    <link rel="stylesheet" href="../style.css">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div style="max-width:1200px; margin:auto;">
<div style="text-align:left;">
<h1 id="ursinamath">
ursinamath
</h1><gray>&lt;/&gt;</gray><a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py"> ursina.ursinamath</a><br>
<h2 id="Functions">
Functions
</h2><h3 id="distance()">
distance()
</h3><code_block id="code_block_0"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_0)">copy</button>distance(a=None, b=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L11" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#distance" onclick="navigator.clipboard.writeText('#distance')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="distance_2d()">
distance_2d()
</h3><code_block id="code_block_1"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_1)">copy</button>distance_<yellow>2</yellow>d(a=None, b=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L28" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#distance_2d" onclick="navigator.clipboard.writeText('#distance_2d')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="distance_xz()">
distance_xz()
</h3><code_block id="code_block_2"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_2)">copy</button>distance_xz(a=None, b=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L35" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#distance_xz" onclick="navigator.clipboard.writeText('#distance_xz')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="lerp()">
lerp()
</h3><code_block id="code_block_3"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_3)">copy</button>lerp(a=None, b=None, t=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L42" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#lerp" onclick="navigator.clipboard.writeText('#lerp')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="inverselerp()">
inverselerp()
</h3><code_block id="code_block_4"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_4)">copy</button>inverselerp(a=None, b=None, value=None)
</code_block>
<purple>get *where* between a and b, value is (0.0 - 1.0)</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L62" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#inverselerp" onclick="navigator.clipboard.writeText('#inverselerp')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="lerp_exponential_decay()">
lerp_exponential_decay()
</h3><code_block id="code_block_5"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_5)">copy</button>lerp_exponential_decay(a=None, b=None, decay_rate=None)
</code_block>
<purple>frame-rate independent lerp for use in update. use this instead of lerp(a, b, time.dt) in update.</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L73" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#lerp_exponential_decay" onclick="navigator.clipboard.writeText('#lerp_exponential_decay')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="lerp_angle()">
lerp_angle()
</h3><code_block id="code_block_6"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_6)">copy</button>lerp_angle(start_angle=None, end_angle=None, t=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L77" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#lerp_angle" onclick="navigator.clipboard.writeText('#lerp_angle')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="slerp()">
slerp()
</h3><code_block id="code_block_7"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_7)">copy</button>slerp(q<yellow>1</yellow>=None, q<yellow>2</yellow>=None, t=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L86" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#slerp" onclick="navigator.clipboard.writeText('#slerp')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="slerp_exponential_decay()">
slerp_exponential_decay()
</h3><code_block id="code_block_8"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_8)">copy</button>slerp_exponential_decay(q<yellow>1</yellow>=None, q<yellow>2</yellow>=None, decay_rate=None)
</code_block>
<purple>frame-rate independent version of slerp for use in update.</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L109" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#slerp_exponential_decay" onclick="navigator.clipboard.writeText('#slerp_exponential_decay')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="clamp()">
clamp()
</h3><code_block id="code_block_9"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_9)">copy</button>clamp(value=None, floor=None, ceiling=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L113" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#clamp" onclick="navigator.clipboard.writeText('#clamp')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="round_to_closest()">
round_to_closest()
</h3><code_block id="code_block_10"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_10)">copy</button>round_to_closest(value=None, step=<yellow>0</yellow>)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L117" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#round_to_closest" onclick="navigator.clipboard.writeText('#round_to_closest')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="rotate_around_point_2d()">
rotate_around_point_2d()
</h3><code_block id="code_block_11"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_11)">copy</button>rotate_around_point_<yellow>2</yellow>d(point=None, <olive>origin</olive>=None, deg=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L125" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#rotate_around_point_2d" onclick="navigator.clipboard.writeText('#rotate_around_point_2d')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="world_position_to_screen_position()">
world_position_to_screen_position()
</h3><code_block id="code_block_12"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_12)">copy</button>world_position_to_screen_position(point=None)
</code_block>
<purple>get screen position(ui space) from world space.</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L137" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#world_position_to_screen_position" onclick="navigator.clipboard.writeText('#world_position_to_screen_position')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="sum()">
sum()
</h3><code_block id="code_block_13"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_13)">copy</button>sum(l=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L145" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#sum" onclick="navigator.clipboard.writeText('#sum')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="make_gradient()">
make_gradient()
</h3><code_block id="code_block_14"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_14)">copy</button>make_gradient(index_value_dict=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L158" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#make_gradient" onclick="navigator.clipboard.writeText('#make_gradient')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="sample_gradient()">
sample_gradient()
</h3><code_block id="code_block_15"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_15)">copy</button>sample_gradient(list_of_values=None, t=None)
</code_block>
<purple>distribute list_of_values equally on a line and get the interpolated value at t (0-1).</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/ursinamath.py#L200" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#sample_gradient" onclick="navigator.clipboard.writeText('#sample_gradient')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h2 id="Examples">
Examples
</h2><br>
<code_block id="code_block_16"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_16)">copy</button>&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.ursinastuff <purple>import</purple> _test
&nbsp;&nbsp;&nbsp;&nbsp;_test(inverselerp(<yellow>0</yellow>, <yellow>1</yellow><yellow>0</yellow><yellow>0</yellow>, <yellow>5</yellow><yellow>0</yellow>) == .<yellow>5</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;_test(lerp(<yellow>0</yellow>, <yellow>1</yellow><yellow>0</yellow><yellow>0</yellow>, .<yellow>5</yellow>) == <yellow>5</yellow><yellow>0</yellow>)


<purple>def</purple> lerp_exponential_decay(a, b, decay_rate):&nbsp;&nbsp;&nbsp;&nbsp;<gray># frame-rate independent lerp <purple>for</purple> use in update. use this instead of lerp(a, b, time.dt) in update.</gray>
&nbsp;&nbsp;&nbsp;&nbsp;return lerp(a, b, <yellow>1</yellow> - pow(<yellow>0</yellow>.<yellow>0</yellow><yellow>1</yellow>, decay_rate))


<purple>def</purple> lerp_angle(start_angle, end_angle, t):
&nbsp;&nbsp;&nbsp;&nbsp;start_angle = start_angle % <yellow>3</yellow><yellow>6</yellow><yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;end_angle = end_angle % <yellow>3</yellow><yellow>6</yellow><yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;angle_diff = (end_angle - start_angle + <yellow>1</yellow><yellow>8</yellow><yellow>0</yellow>) % <yellow>3</yellow><yellow>6</yellow><yellow>0</yellow> - <yellow>1</yellow><yellow>8</yellow><yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;result_angle = start_angle + t * angle_diff
&nbsp;&nbsp;&nbsp;&nbsp;result_angle = (result_angle + <yellow>3</yellow><yellow>6</yellow><yellow>0</yellow>) % <yellow>3</yellow><yellow>6</yellow><yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;return result_angle


<purple>def</purple> slerp(q<yellow>1</yellow>, q<yellow>2</yellow>, t):
&nbsp;&nbsp;&nbsp;&nbsp;costheta = q<yellow>1</yellow>.dot(q<yellow>2</yellow>)

&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> costheta &lt; <yellow>0</yellow>.<yellow>0</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q<yellow>2</yellow> = -q<yellow>2</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;costheta = -costheta

&nbsp;&nbsp;&nbsp;&nbsp;costheta = clamp(costheta, -<yellow>1</yellow>.<yellow>0</yellow>, <yellow>1</yellow>.<yellow>0</yellow>)&nbsp;&nbsp; <gray># ensure valid range <purple>for</purple> acos</gray>

&nbsp;&nbsp;&nbsp;&nbsp;theta = acos(costheta)
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> abs(theta) &lt; <yellow>0</yellow>.<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>1</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return q<yellow>2</yellow>

&nbsp;&nbsp;&nbsp;&nbsp;sintheta = sqrt(<yellow>1</yellow>.<yellow>0</yellow> - costheta * costheta)
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> abs(sintheta) &lt; <yellow>0</yellow>.<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>1</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (q<yellow>1</yellow> + q<yellow>2</yellow>) * <yellow>0</yellow>.<yellow>5</yellow>

&nbsp;&nbsp;&nbsp;&nbsp;r<yellow>1</yellow> = sin((<yellow>1</yellow>.<yellow>0</yellow> - t) * theta) / sintheta
&nbsp;&nbsp;&nbsp;&nbsp;r<yellow>2</yellow> = sin(t * theta) / sintheta
&nbsp;&nbsp;&nbsp;&nbsp;return (q<yellow>1</yellow> * r<yellow>1</yellow>) + (q<yellow>2</yellow> * r<yellow>2</yellow>)


<purple>def</purple> slerp_exponential_decay(q<yellow>1</yellow>, q<yellow>2</yellow>, decay_rate):&nbsp;&nbsp;&nbsp;&nbsp;<gray># frame-rate independent version of slerp <purple>for</purple> use in update.</gray>
&nbsp;&nbsp;&nbsp;&nbsp;return slerp(q<yellow>1</yellow>, q<yellow>2</yellow>, <yellow>1</yellow> - pow(<yellow>0</yellow>.<yellow>0</yellow><yellow>1</yellow>, decay_rate))


<purple>def</purple> clamp(value, floor, ceiling):
&nbsp;&nbsp;&nbsp;&nbsp;return max(min(value, ceiling), floor)


<purple>def</purple> round_to_closest(value, step=<yellow>0</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> <purple>not</purple> step:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return value

&nbsp;&nbsp;&nbsp;&nbsp;step = <yellow>1</yellow>/step
&nbsp;&nbsp;&nbsp;&nbsp;return round(value * step) / step


<purple>def</purple> rotate_around_point_<yellow>2</yellow>d(point, origin, deg):
&nbsp;&nbsp;&nbsp;&nbsp;angle_rad = -deg/<yellow>1</yellow><yellow>8</yellow><yellow>0</yellow> * pi <gray># ursina rotation is positive=clockwise, so do *= -<yellow>1</yellow></gray>
&nbsp;&nbsp;&nbsp;&nbsp;cos_angle = cos(angle_rad)
&nbsp;&nbsp;&nbsp;&nbsp;sin_angle = sin(angle_rad)
&nbsp;&nbsp;&nbsp;&nbsp;dx = point[<yellow>0</yellow>] - origin[<yellow>0</yellow>]
&nbsp;&nbsp;&nbsp;&nbsp;dy = point[<yellow>1</yellow>] - origin[<yellow>1</yellow>]

&nbsp;&nbsp;&nbsp;&nbsp;return (
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;origin[<yellow>0</yellow>] + (dx*cos_angle - dy*sin_angle),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;origin[<yellow>1</yellow>] + (dx*sin_angle + dy*cos_angle)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)

<purple>def</purple> world_position_to_screen_position(point): <gray># get screen position(ui space) <purple>from</purple> world space.</gray>
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina <purple>import</purple> camera, <olive>Entity</olive>, destroy
&nbsp;&nbsp;&nbsp;&nbsp;_temp_entity = <olive>Entity</olive>(<olive>position</olive>=point, add_to_scene_entities=False)
&nbsp;&nbsp;&nbsp;&nbsp;result = _temp_entity.screen_position
&nbsp;&nbsp;&nbsp;&nbsp;destroy(_temp_entity)
&nbsp;&nbsp;&nbsp;&nbsp;return result


<purple>def</purple> sum(l):
&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return _sum(l)
&nbsp;&nbsp;&nbsp;&nbsp;except:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pass

&nbsp;&nbsp;&nbsp;&nbsp;total = l[<yellow>0</yellow>].__class__()
&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> e in l:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total += e

&nbsp;&nbsp;&nbsp;&nbsp;return total


<purple>def</purple> make_gradient(index_value_dict):
&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green>'
&nbsp;&nbsp;&nbsp;&nbsp;given a dict of positions and values (usually colors), interpolates the values into a list of with the interpolated values.
&nbsp;&nbsp;&nbsp;&nbsp;example input: {<green>'<yellow>0</yellow>'</green>:color.hex(<green>'<gray>#<yellow>9</yellow>d<yellow>9</yellow><yellow>8</yellow><yellow>6</yellow><yellow>7</yellow>'</green>), <green>'<yellow>3</yellow><yellow>8</yellow>'</green>:color.hex(<green>'<gray>#<yellow>8</yellow><yellow>2</yellow><yellow>8</yellow><yellow>1</yellow><yellow>3</yellow><yellow>1</yellow>'</green>), <green>'<yellow>5</yellow><yellow>4</yellow>'</green>:color.hex(<green>'<gray>#<yellow>5</yellow>d<yellow>5</yellow>b<yellow>2</yellow>a'</green>), <green>'<yellow>2</yellow><yellow>5</yellow><yellow>5</yellow>'</green>:color.hex(<green>'<gray>#<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>'</green>)}</gray>
&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green>'
&nbsp;&nbsp;&nbsp;&nbsp;min_index = min(int(e) <purple>for</purple> e in index_value_dict.keys())
&nbsp;&nbsp;&nbsp;&nbsp;max_index = max(int(e) <purple>for</purple> e in index_value_dict.keys())
&nbsp;&nbsp;&nbsp;&nbsp;gradient = [None <purple>for</purple> _ in <blue>range</blue>(max_index+<yellow>1</yellow>-min_index)]

&nbsp;&nbsp;&nbsp;&nbsp;sorted_dict = [(idx, index_value_dict[str(idx)]) <purple>for</purple> idx in sorted([int(key) <purple>for</purple> key in index_value_dict.keys()])]

&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> i in <blue>range</blue>(len(sorted_dict)-<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start_index, start_value = sorted_dict[i]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next_index, next_value = sorted_dict[i+<yellow>1</yellow>]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dist = next_index - start_index
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> j in <blue>range</blue>(dist+<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gradient[start_index+j-min_index] = lerp(start_value, next_value, j/dist)

&nbsp;&nbsp;&nbsp;&nbsp;return gradient

<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;_test(make_gradient({<green>'<yellow>0</yellow>'</green>:color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), <green>'<yellow>2</yellow>'</green>:color.hex(<green>'<gray>#ffffffff'</green>)}) == [</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lerp(color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), color.hex(<green>'<gray>#ffffffff'</green>), .<yellow>5</yellow>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.hex(<green>'<gray>#ffffffff'</green>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;])
&nbsp;&nbsp;&nbsp;&nbsp;_test(make_gradient({<green>'<yellow>0</yellow>'</green>:color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), <green>'<yellow>4</yellow>'</green>:color.hex(<green>'<gray>#ffffffff'</green>)}) == [</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lerp(color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), color.hex(<green>'<gray>#ffffffff'</green>), .<yellow>2</yellow><yellow>5</yellow>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lerp(color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), color.hex(<green>'<gray>#ffffffff'</green>), .<yellow>5</yellow>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lerp(color.hex(<green>'<gray>#ff<yellow>0</yellow><yellow>0</yellow><yellow>0</yellow><yellow>0</yellow>ff'</green>), color.hex(<green>'<gray>#ffffffff'</green>), .<yellow>7</yellow><yellow>5</yellow>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color.hex(<green>'<gray>#ffffffff'</green>),</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;])
&nbsp;&nbsp;&nbsp;&nbsp;_test(make_gradient({<green>'<yellow>0</yellow>'</green>:<yellow>1</yellow><yellow>6</yellow>, <green>'<yellow>2</yellow>'</green>:<yellow>0</yellow>}) == [<yellow>1</yellow><yellow>6</yellow>, <yellow>8</yellow>, <yellow>0</yellow>])

&nbsp;&nbsp;&nbsp;&nbsp;_test(make_gradient({<green>'<yellow>6</yellow>'</green>:<yellow>0</yellow>, <green>'<yellow>8</yellow>'</green>:<yellow>8</yellow>}) == [<yellow>0</yellow>, <yellow>4</yellow>, <yellow>8</yellow>])


<purple>def</purple> sample_gradient(list_of_values, t):&nbsp;&nbsp;&nbsp;&nbsp; <gray># distribute list_of_values equally on a line and get the interpolated value at t (<yellow>0</yellow>-<yellow>1</yellow>).</gray>
&nbsp;&nbsp;&nbsp;&nbsp;l = len(list_of_values)
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> l == <yellow>1</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return list_of_values[<yellow>0</yellow>]

&nbsp;&nbsp;&nbsp;&nbsp;t *= l-<yellow>1</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;index = floor(t - .<yellow>0</yellow><yellow>0</yellow><yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;index = clamp(index, <yellow>0</yellow>, l-<yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;relative = t - index

&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> index &lt; l-<yellow>1</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return lerp(list_of_values[index], list_of_values[index+<yellow>1</yellow>], relative)
&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return lerp(list_of_values[index-<yellow>1</yellow>], list_of_values[index], relative)



class Bounds:
&nbsp;&nbsp;&nbsp;&nbsp;__slots__ = [<green>'start'</green>, <green>'end'</green>, <green>'center'</green>, <green>'size'</green>]

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __init__(self, *, start:Vec<yellow>3</yellow>=None, end:Vec<yellow>3</yellow>=None, center:Vec<yellow>3</yellow>=None, size:Vec<yellow>3</yellow>=None):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> start is <purple>not</purple> None and end is <purple>not</purple> None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.start = start
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.end = end
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.size = end - start
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.center = start + (self.size * <yellow>0</yellow>.<yellow>5</yellow>)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>elif</purple> center is <purple>not</purple> None and size is <purple>not</purple> None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.center = center
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.size = size
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;half = size * <yellow>0</yellow>.<yellow>5</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.start = center - half
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.end = center + half

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("Must provide either (start and end) or (center and size)")

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __repr__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return f"Bounds(start={self.start}, end={self.end}, center={self.center}, size={self.size})"


<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina <purple>import</purple> *
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursinastuff <purple>import</purple> _test
&nbsp;&nbsp;&nbsp;&nbsp;app = Ursina()
&nbsp;&nbsp;&nbsp;&nbsp;e<yellow>1</yellow> = <olive>Entity</olive>(position = (<yellow>0</yellow>,<yellow>0</yellow>,<yellow>0</yellow>))
&nbsp;&nbsp;&nbsp;&nbsp;e<yellow>2</yellow> = <olive>Entity</olive>(position = (<yellow>0</yellow>,<yellow>1</yellow>,<yellow>1</yellow>))
&nbsp;&nbsp;&nbsp;&nbsp;_test(distance(e<yellow>1</yellow>, e<yellow>2</yellow>) == <yellow>1</yellow>.<yellow>4</yellow><yellow>1</yellow><yellow>4</yellow><yellow>2</yellow><yellow>1</yellow><yellow>3</yellow><yellow>5</yellow><yellow>6</yellow><yellow>2</yellow><yellow>3</yellow><yellow>7</yellow><yellow>3</yellow><yellow>0</yellow><yellow>9</yellow><yellow>5</yellow><yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;_test(distance_<yellow>2</yellow>d(Vec2(<yellow>0</yellow>,<yellow>0</yellow>), Vec2(<yellow>1</yellow>,<yellow>1</yellow>)) == <yellow>1</yellow>.<yellow>4</yellow><yellow>1</yellow><yellow>4</yellow><yellow>2</yellow><yellow>1</yellow><yellow>3</yellow><yellow>5</yellow><yellow>6</yellow><yellow>2</yellow><yellow>3</yellow><yellow>7</yellow><yellow>3</yellow><yellow>0</yellow><yellow>9</yellow><yellow>5</yellow><yellow>1</yellow>)

&nbsp;&nbsp;&nbsp;&nbsp;distance_xz(e<yellow>1</yellow>, e<yellow>2</yellow>.position)

&nbsp;&nbsp;&nbsp;&nbsp;between_color = lerp(color.lime, color.magenta, .<yellow>5</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(between_color)
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(lerp((<yellow>0</yellow>,<yellow>0</yellow>), (<yellow>0</yellow>,<yellow>1</yellow>), .<yellow>5</yellow>))
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(lerp(Vec2(<yellow>0</yellow>,<yellow>0</yellow>), Vec2(<yellow>0</yellow>,<yellow>1</yellow>), .<yellow>5</yellow>))
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(lerp([<yellow>0</yellow>,<yellow>0</yellow>], [<yellow>0</yellow>,<yellow>1</yellow>], .<yellow>5</yellow>))

&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(round(Vec3(.<yellow>3</yellow><yellow>8</yellow>, .<yellow>1</yellow><yellow>3</yellow><yellow>5</yellow><yellow>1</yellow>, <yellow>3</yellow><yellow>5</yellow><yellow>3</yellow>.<yellow>2</yellow><yellow>6</yellow>), <yellow>2</yellow>))

&nbsp;&nbsp;&nbsp;&nbsp;p = (<yellow>1</yellow>,<yellow>0</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(p, <green>'rotated -&gt;'</green>, rotate_around_point_<yellow>2</yellow>d(p, (<yellow>0</yellow>,<yellow>0</yellow>), <yellow>9</yellow><yellow>0</yellow>))

&nbsp;&nbsp;&nbsp;&nbsp;app.run()

</code_block>
<div class="sidebar"><span>8.0.1</span><br>
Basics<br>
&nbsp;&nbsp;<a href="ursina.html">Ursina</a><br>
&nbsp;&nbsp;<a href="entity.html">Entity</a><br>
&nbsp;&nbsp;<a href="button.html">Button</a><br>
&nbsp;&nbsp;<a href="sprite.html">Sprite</a><br>
&nbsp;&nbsp;<a href="text.html">Text</a><br>
&nbsp;&nbsp;<a href="audio.html">Audio</a><br>
<br>
Core Modules<br>
&nbsp;&nbsp;<a href="camera.html">camera</a><br>
&nbsp;&nbsp;<a href="mouse.html">mouse</a><br>
&nbsp;&nbsp;<a href="window.html">window</a><br>
&nbsp;&nbsp;<a href="application.html">application</a><br>
&nbsp;&nbsp;<a href="scene.html">scene</a><br>
<br>
Graphics<br>
&nbsp;&nbsp;<a href="color.html">color</a><br>
&nbsp;&nbsp;<a href="mesh.html">Mesh</a><br>
&nbsp;&nbsp;<a href="shader.html">Shader</a><br>
&nbsp;&nbsp;<a href="texture.html">Texture</a><br>
&nbsp;&nbsp;<a href="light.html">Light</a><br>
&nbsp;&nbsp;<a href="directional_light.html">DirectionalLight</a><br>
&nbsp;&nbsp;<a href="point_light.html">PointLight</a><br>
&nbsp;&nbsp;<a href="ambient_light.html">AmbientLight</a><br>
&nbsp;&nbsp;<a href="spot_light.html">SpotLight</a><br>
<br>
Procedural Models<br>
&nbsp;&nbsp;<a href="quad.html">Quad</a><br>
&nbsp;&nbsp;<a href="circle.html">Circle</a><br>
&nbsp;&nbsp;<a href="plane.html">Plane</a><br>
&nbsp;&nbsp;<a href="grid.html">Grid</a><br>
&nbsp;&nbsp;<a href="cone.html">Cone</a><br>
&nbsp;&nbsp;<a href="cylinder.html">Cylinder</a><br>
&nbsp;&nbsp;<a href="pipe.html">Pipe</a><br>
&nbsp;&nbsp;<a href="terrain.html">Terrain</a><br>
<br>
modules<br>
&nbsp;&nbsp;<a href="input_handler.html">input_handler</a><br>
&nbsp;&nbsp;<a href="mesh_importer.html">mesh_importer</a><br>
&nbsp;&nbsp;<a href="texture_importer.html">texture_importer</a><br>
&nbsp;&nbsp;<a href="string_utilities.html">string_utilities</a><br>
<br>
Animation<br>
&nbsp;&nbsp;<a href="animation.html">Animation</a><br>
&nbsp;&nbsp;<a href="frame_animation3d.html">FrameAnimation3d</a><br>
&nbsp;&nbsp;<a href="sprite_sheet_animation.html">SpriteSheetAnimation</a><br>
&nbsp;&nbsp;<a href="animator.html">Animator</a><br>
&nbsp;&nbsp;<a href="trail_renderer.html">TrailRenderer</a><br>
&nbsp;&nbsp;<a href="curve.html">curve</a><br>
<br>
Math<br>
&nbsp;&nbsp;<a href="ursinamath.html">ursinamath</a><br>
&nbsp;&nbsp;<a href="vec2.html">Vec2</a><br>
&nbsp;&nbsp;<a href="vec3.html">Vec3</a><br>
&nbsp;&nbsp;<a href="vec4.html">Vec4</a><br>
&nbsp;&nbsp;<a href="cubic_bezier.html">CubicBezier</a><br>
&nbsp;&nbsp;<a href="array_tools.html">array_tools</a><br>
&nbsp;&nbsp;<a href="array2_d.html">Array2D</a><br>
&nbsp;&nbsp;<a href="array3_d.html">Array3D</a><br>
<br>
Gameplay<br>
&nbsp;&nbsp;<a href="ursinastuff.html">ursinastuff</a><br>
&nbsp;&nbsp;<a href="sequence.html">Sequence</a><br>
&nbsp;&nbsp;<a href="func.html">Func</a><br>
&nbsp;&nbsp;<a href="keys.html">Keys</a><br>
<br>
Collision<br>
&nbsp;&nbsp;<a href="raycast.html">raycast</a><br>
&nbsp;&nbsp;<a href="terraincast.html">terraincast</a><br>
&nbsp;&nbsp;<a href="boxcast.html">boxcast</a><br>
&nbsp;&nbsp;<a href="hit_info.html">HitInfo</a><br>
&nbsp;&nbsp;<a href="collider.html">Collider</a><br>
&nbsp;&nbsp;<a href="box_collider.html">BoxCollider</a><br>
&nbsp;&nbsp;<a href="sphere_collider.html">SphereCollider</a><br>
&nbsp;&nbsp;<a href="mesh_collider.html">MeshCollider</a><br>
<br>
Prefabs<br>
&nbsp;&nbsp;<a href="sky.html">Sky</a><br>
&nbsp;&nbsp;<a href="editor_camera.html">EditorCamera</a><br>
&nbsp;&nbsp;<a href="tilemap.html">Tilemap</a><br>
&nbsp;&nbsp;<a href="first_person_controller.html">FirstPersonController</a><br>
&nbsp;&nbsp;<a href="platformer_controller2d.html">PlatformerController2d</a><br>
&nbsp;&nbsp;<a href="conversation.html">Conversation</a><br>
<br>
UI<br>
&nbsp;&nbsp;<a href="button.html">Button</a><br>
&nbsp;&nbsp;<a href="draggable.html">Draggable</a><br>
&nbsp;&nbsp;<a href="tooltip.html">Tooltip</a><br>
&nbsp;&nbsp;<a href="slider.html">Slider</a><br>
&nbsp;&nbsp;<a href="thin_slider.html">ThinSlider</a><br>
&nbsp;&nbsp;<a href="text_field.html">TextField</a><br>
&nbsp;&nbsp;<a href="input_field.html">InputField</a><br>
&nbsp;&nbsp;<a href="content_types.html">ContentTypes</a><br>
&nbsp;&nbsp;<a href="checkbox.html">Checkbox</a><br>
&nbsp;&nbsp;<a href="button_list.html">ButtonList</a><br>
&nbsp;&nbsp;<a href="button_group.html">ButtonGroup</a><br>
&nbsp;&nbsp;<a href="window_panel.html">WindowPanel</a><br>
&nbsp;&nbsp;<a href="file_browser.html">FileBrowser</a><br>
&nbsp;&nbsp;<a href="file_browser_save.html">FileBrowserSave</a><br>
&nbsp;&nbsp;<a href="dropdown_menu.html">DropdownMenu</a><br>
&nbsp;&nbsp;<a href="radial_menu.html">RadialMenu</a><br>
&nbsp;&nbsp;<a href="health_bar.html">HealthBar</a><br>
<br>
Editor<br>
&nbsp;&nbsp;<a href="hot_reloader.html">HotReloader</a><br>
&nbsp;&nbsp;<a href="grid_editor.html">GridEditor</a><br>
&nbsp;&nbsp;<a href="pixel_editor.html">PixelEditor</a><br>
&nbsp;&nbsp;<a href="ascii_editor.html">ASCIIEditor</a><br>
&nbsp;&nbsp;<a href="color_picker.html">ColorPicker</a><br>
<br>
Scripts<br>
&nbsp;&nbsp;<a href="grid_layout.html">grid_layout</a><br>
&nbsp;&nbsp;<a href="duplicate.html">duplicate</a><br>
&nbsp;&nbsp;<a href="smooth_follow.html">SmoothFollow</a><br>
&nbsp;&nbsp;<a href="scrollable.html">Scrollable</a><br>
&nbsp;&nbsp;<a href="noclip_mode.html">NoclipMode</a><br>
&nbsp;&nbsp;<a href="noclip_mode2d.html">NoclipMode2d</a><br>
&nbsp;&nbsp;<a href="build.html">build</a><br>
<br>
Assets<br>
&nbsp;&nbsp;<a href="models.html">models</a><br>
&nbsp;&nbsp;<a href="textures.html">textures</a><br>
<br>
Shaders<br>
&nbsp;&nbsp;<a href="unlit_shader.html">unlit_shader</a><br>
&nbsp;&nbsp;<a href="lit_with_shadows_shader.html">lit_with_shadows_shader</a><br>
&nbsp;&nbsp;<a href="matcap_shader.html">matcap_shader</a><br>
&nbsp;&nbsp;<a href="colored_lights_shader.html">colored_lights_shader</a><br>
&nbsp;&nbsp;<a href="fresnel_shader.html">fresnel_shader</a><br>
&nbsp;&nbsp;<a href="projector_shader.html">projector_shader</a><br>
&nbsp;&nbsp;<a href="texture_blend_shader.html">texture_blend_shader</a><br>
&nbsp;&nbsp;<a href="instancing_shader.html">instancing_shader</a><br>
&nbsp;&nbsp;<a href="triplanar_shader.html">triplanar_shader</a><br>
&nbsp;&nbsp;<a href="normals_shader.html">normals_shader</a><br>
&nbsp;&nbsp;<a href="transition_shader.html">transition_shader</a><br>
&nbsp;&nbsp;<a href="fxaa.html">fxaa</a><br>
&nbsp;&nbsp;<a href="ssao.html">ssao</a><br>
&nbsp;&nbsp;<a href="camera_outline_shader.html">camera_outline_shader</a><br>
&nbsp;&nbsp;<a href="pixelation_shader.html">pixelation_shader</a><br>
&nbsp;&nbsp;<a href="camera_contrast.html">camera_contrast</a><br>
&nbsp;&nbsp;<a href="camera_vertical_blur.html">camera_vertical_blur</a><br>
&nbsp;&nbsp;<a href="camera_grayscale.html">camera_grayscale</a><br>
</div><br>
<script>
function copy_to_clipboard(containerid) {
    var range = document.createRange()
    range.selectNode(containerid)
    window.getSelection().removeAllRanges()
    window.getSelection().addRange(range)
    document.execCommand("copy")
    window.getSelection().removeAllRanges()
}
</script>
<br>
<br>
</body>
</html><style>
h1 {margin-bottom:0em; margin-top:1em}
h2 {margin-bottom:.5em;}
h3 {margin:0em; font-size:1em;}
code_block {margin-top:.0em; margin-bottom:1em;}
hr {border-color:gray; opacity:.25;}
.links {width:fit-content;}
.links a {text-decoration:none; opacity:.5;}
</style>
